package cgroup

import (
	"errors"
	"fmt"
	"os"
	"path/filepath"
)

const (
	Memory Key = "memory"
	CPUSet Key = "cpuset"
	CPU    Key = "cpu"
)

var (
	ErrNotAvailable = errors.New("not available")
	ErrNil          = errors.New("nil")
)

type Key string

func (s Key) String() string {
	return string(s)
}

//CGroup 封装
type CGroup struct {
	gm  map[Key]string
	err error
}

// Open 指定目录打开
func Open(baseDIR string) (g *CGroup) {
	g = &CGroup{
		gm: map[Key]string{
			Memory: filepath.Join(baseDIR, Memory.String()),
			CPUSet: filepath.Join(baseDIR, CPUSet.String()),
			CPU:    filepath.Join(baseDIR, CPU.String()),
		},
	}
	return
}

// Subsystem 获取子系统
func (g *CGroup) Subsystem(key Key, children ...string) (*Subsystem, error) {
	if g.err != nil {
		return nil, g.err
	}
	if base := g.gm[key]; base != "" {
		s := Subsystem(filepath.Join(append([]string{base}, children...)...))
		return &s, nil
	}
	return nil, ErrNotAvailable
}

// Subsystem 子系统
type Subsystem string

// IsNil 空对象
func (s *Subsystem) IsNil() bool {
	return s == nil || *s == ""
}

func (s *Subsystem) String() string {
	if s != nil {
		return string(*s)
	}
	return "<nil>"
}

// Exist 是否存在
func (s *Subsystem) Exist() bool {
	if s.IsNil() {
		return false
	}
	fi, _ := os.Stat(s.String())
	if fi != nil {
		return fi.IsDir()
	}
	return false
}

// Create 创建
func (s *Subsystem) Create() error {
	if s.IsNil() {
		return ErrNil
	}
	if err := os.Mkdir(s.String(), os.ModePerm); err != nil {
		return fmt.Errorf("create: %s: %w", s, err)
	}
	return nil
}

// Delete 删除
func (s *Subsystem) Delete() error {
	if s.Exist() {
		if err := os.RemoveAll(s.String()); err != nil {
			return fmt.Errorf("delete: %s: %w", s, err)
		}
	}
	return nil
}

// File 获取文件
func (s *Subsystem) File(name string) (*File, error) {
	fn := filepath.Join(s.String(), name)
	fi, err := os.Stat(fn)
	if err != nil {
		return nil, err
	}
	if fi.IsDir() {
		return nil, fmt.Errorf("not a file: %s", fn)
	}
	f := File(fn)
	return &f, nil
}

// Files 列出所有文件
func (s *Subsystem) Files() (files []*File, err error) {
	if s.IsNil() {
		return nil, ErrNil
	}
	var dirs []os.DirEntry
	if dirs, err = os.ReadDir(s.String()); err != nil {
		return
	}
	for _, e := range dirs {
		if !e.IsDir() {
			f := File(filepath.Join(s.String(), e.Name()))
			files = append(files, &f)
		}
	}
	return
}

// Children 获取子系统
func (s *Subsystem) Children() (subs []*Subsystem, err error) {
	if s.IsNil() {
		return nil, ErrNil
	}
	var dirs []os.DirEntry
	if dirs, err = os.ReadDir(s.String()); err != nil {
		return
	}
	for _, e := range dirs {
		if e.IsDir() {
			sub := Subsystem(filepath.Join(s.String(), e.Name()))
			subs = append(subs, &sub)
		}
	}
	return
}

// File 文件
type File string

// IsNil 是否空
func (s *File) IsNil() bool {
	return s == nil || *s == ""
}

func (s *File) String() string {
	if s != nil {
		return string(*s)
	}
	return "<nil>"
}

//Write 写入
func (s *File) Write(data string) error {
	if s.IsNil() {
		return ErrNil
	}
	f, err := os.OpenFile(s.String(), os.O_WRONLY|os.O_TRUNC, 0)
	if err != nil {
		return err
	}
	defer func(f *os.File) { _ = f.Close() }(f)
	_, err = fmt.Fprintln(f, data)
	return err
}

// Append 追加
func (s *File) Append(data string) error {
	if s.IsNil() {
		return ErrNil
	}
	f, err := os.OpenFile(s.String(), os.O_WRONLY|os.O_APPEND, 0)
	if err != nil {
		return err
	}
	defer func(f *os.File) { _ = f.Close() }(f)
	_, err = fmt.Fprintln(f, data)
	return err
}
